#include "SDFDofRange.h"
#include <qmath.h>
#include <iostream>

Range1D::Range1D()
{
    max = 0.0;
    min = 0.0;

    minClosed = false;
    maxClosed = false;
    isValid = true;
}

Range1D::~Range1D()
{

}

Range1D Range1D::operator&(Range1D &t)
{
    Range1D n;
    n.isValid = true;

    if(isValid == false || t.isValid == false)
    {
        n.isValid = false;
        return n;
    }

    double middleDistanceT = 0.5*(max + min) - 0.5*(t.max + t.min);
    double m_lengthT = max - min;
    double t_lengthT = t.max - t.min;

    if(qAbs(middleDistanceT - 0) < 10e-5)
    {
        n.min = min;
        n.max = max;
        n.minClosed = minClosed & t.minClosed;
        n.maxClosed = maxClosed & t.maxClosed;
    }
    else if(qAbs(middleDistanceT) < 0.5*m_lengthT + 0.5*t_lengthT)
    {
        //is inserted
        n.min = min > t.min ? min : t.min;
        n.max = max < t.max ? max : t.max;
        n.minClosed = min > t.min ? minClosed : t.minClosed;
        n.maxClosed = max < t.max ? maxClosed : t.maxClosed;

    }
    else if(qAbs(middleDistanceT) > 0.5*m_lengthT + 0.5*t_lengthT)
    {
        n.isValid = false;
    }
    else
    {
        if(middleDistanceT > 0 && minClosed == true && t.maxClosed == true)
        {
            n.min = min;
            n.max = min;
            n.minClosed = true;
            n.maxClosed = true;
        }
        else if(middleDistanceT < 0 && maxClosed == true && t.minClosed == true)
        {
            n.min = max;
            n.max = max;
            n.minClosed = true;
            n.maxClosed = true;
        }
        else
        {
            n.isValid = false;
        }
    }

    return n;
}


Range1D Range1D::andOperatorAsAngle(Range1D &t)
{
    Range1D n;
    Range1D m1 = this->transRangeAsAngleNearZero();
    Range1D m2 = t.transRangeAsAngleNearZero();
    n = m1 & m2;

    return n;
}

Range1D Range1D::orOperatorAsAngle(Range1D &t1, Range1D &t2)
{
    Range1D n;

    return n;
}

void Range1D::twoRangeAsAngle(Range1D &t1, Range1D &t2)
{
    //double angle = 0.5*(max + min);
    if(min < 0)
    {
        t1.min = 0;
        t1.max = max;
        t1.minClosed = true;
        t1.maxClosed = maxClosed;

        t2.min = 2*osg::PI + min;
        t2.max = 2*osg::PI;
        t2.minClosed = minClosed;
        t2.maxClosed = true;
    }
    else if(max > 2*osg::PI)
    {
        t1.min = min;
        t1.max = 2*osg::PI;
        t1.minClosed = minClosed;
        t1.maxClosed = true;

        t2.min = 0;
        t2.max = max - 2*osg::PI;
        t2.minClosed = true;
        t2.maxClosed = maxClosed;
    }
    else
    {
        t1.min = min;
        t1.max = max;
        t1.minClosed = minClosed;
        t1.maxClosed = maxClosed;
        t2.isValid = false;
    }
}

Range1D Range1D::transRangeAsAngleNearZero()
{
    Range1D t1;
    if(min < 0)
    {
        t1.min = min;
        t1.max = max;
        t1.minClosed = minClosed;
        t1.maxClosed = maxClosed;
    }
    else if(max > 2*osg::PI)
    {
        t1.min = min - 2*osg::PI;
        t1.max = max - 2*osg::PI;
        t1.minClosed = minClosed;
        t1.maxClosed = maxClosed;
    }
    else
    {
        t1.min = min;
        t1.max = max;
        t1.minClosed = minClosed;
        t1.maxClosed = maxClosed;
    }
    return t1;
}

/*
Range1D andOperatorAsAngle2(Range1D &t)
{
    Range1D n;

    double angle1 = 0.5*(max + min);
    double angle2 = 0.5*(t.max + t.min);
    double angleDistance = angle1 - angle2;
    double absAngleDistance = qAbs(angleDistance) > osg::PI ? 2*osg::PI - qAbs(angleDistance): qAbs(angleDistance);

    if(absAngleDistance - 0 < 10e-5)
    {
        n.max = max;
        n.min = min;
        n.maxClosed = maxClosed & t.maxClosed;
        n.minClosed = minClosed & t.minClosed;
    }
    else if(absAngleDistance < 0.5*osg::PI)
    {
        if(angle1 > angle2)
        {
            n.max = t.max;
            n.min = min;
            n.maxClosed = t.maxClosed;
            n.minClosed = minClosed;
        }
        else if(angle1 < angle2)
        {
            n.max = max;
            n.min = t.min;
            n.maxClosed = maxClosed;
            n.minClosed = t.minClosed;
        }
    }
    else if(qAbs(absAngleDistance - 0.5*osg::PI) < 10e-5)
    {
        if(angle1 > angle2)
        {
            n.max = angle1;
            n.min = angle2;
            n.maxClosed = t.maxClosed;
            n.minClosed = minClosed;
        }
        else if(angle1 < angle2)
        {
            n.max = angle2;
            n.min = angle;
            n.maxClosed = maxClosed;
            n.minClosed = t.minClosed;
        }
    }
    else if(absAngleDistance > 0.5*osg::PI)
    {

    }
    else if(abs(angleDistance - 0.5*osg::PI) < 10e-5)
    {

    }

    return n;
}
*/


SDFDofRange::SDFDofRange() : osg::Node()
{
    m_minS = 0.0;
    m_minT = 0.0;
    m_maxS = 0.0;
    m_maxT = 0.0;

    m_minSClosed = false;
    m_maxSClosed = false;
    m_minTClosed = false;
    m_maxTClosed = false;

    m_isValid = true;
}

SDFDofRange::~SDFDofRange()
{

}

SDFDofRange SDFDofRange::operator&( SDFDofRange &t)
{    
    SDFDofRange newRange;
    if(m_isValid == false || t.getIsValid() == false)
    {
        newRange.setIsValid(false);
        return newRange;
    }

    newRange.m_rangeT = m_rangeT & t.m_rangeT;

    newRange.m_rangeS = m_rangeS.andOperatorAsAngle(t.m_rangeS);

    return newRange;
}

double SDFDofRange::getMinS()
{
    return m_minS;
}

double SDFDofRange::getMaxS()
{
    return m_maxS;
}

double SDFDofRange::getMinT()
{
    return m_minT;
}

double SDFDofRange::getMaxT()
{
    return m_maxT;

}

bool SDFDofRange::getMinSClosed()
{
    return m_minSClosed;

}

bool SDFDofRange::getMaxSClosed()
{
    return m_maxSClosed;

}

bool SDFDofRange::getMinTClosed()
{
    return m_minTClosed;

}

bool SDFDofRange::getMaxTClosed()
{
    return m_maxTClosed;

}

void SDFDofRange::computeRangeFromNormal(const osg::Vec3 &normal)
{
    double s = computeRangeSAngle(normal);
    double t = computeRangeTAngle(normal);

    m_rangeS.min = s - 0.5*osg::PI;
    m_rangeS.max = s + 0.5*osg::PI;

    m_rangeT.min = t - 0.5*osg::PI;
    m_rangeT.max = t + 0.5*osg::PI;


}

bool SDFDofRange::getIsValid()
{
    return m_isValid;
}

void SDFDofRange::print()
{
    std::cout<<"-------------------------------"<<std::endl;
    std::cout<<"SDFDofRange valid= "<<m_isValid<<std::endl;
    std::cout<<"SDFDofRange t= "<<m_rangeT.min<<", "<<m_rangeT.max<<std::endl;
    std::cout<<"SDFDofRange tClosed= "<<m_rangeT.minClosed<<", "<<m_rangeT.maxClosed<<std::endl;
    std::cout<<"SDFDofRange s= "<<m_rangeS.min<<", "<<m_rangeS.max<<std::endl;
    std::cout<<"SDFDofRange sClosed= "<<m_rangeS.minClosed<<", "<<m_rangeS.maxClosed<<std::endl;
    std::cout<<"-------------------------------"<<std::endl;

}

double SDFDofRange::computeRangeSAngle(const osg::Vec3 &normal)
{
    double r = qSqrt( normal.x()*normal.x() + normal.y()*normal.y() );//normal.length();
    if(qAbs(r - 0) < 10e-5)
    {
        return 0;
    }
    double sinA = normal.y() / r;
    double cosA = normal.x() / r;

    double A = qAsin(sinA);

    if(sinA > 0)
    {
        if(cosA < 0)
        {
            A = osg::PI - A;
        }
    }
    else if(qAbs(sinA - 0) < 10e-5 )
    {
        if(cosA < 0 )
        {
            A = osg::PI;
        }
        else if(cosA > 0)
        {
            A = 0;
        }
        else
        {
            A = 0;
        }
    }
    else if(sinA < 0)
    {
        if(cosA < 0)
        {
            A = osg::PI - A;
        }
        else if(cosA > 0)
        {
            A = 2*osg::PI + A;
        }
        else
        {
            A = 2*osg::PI + A;
        }
    }
    //std::cout<<"SDFDofRange::computeRangeFromNormal S="<<s<<std::endl;
    return A;
}

double SDFDofRange::computeRangeTAngle(const osg::Vec3 &normal)
{
    double r = normal.length();
    double sinA = normal.z() / r;
    double cosA = normal.x() / r;

    double A = qAsin(sinA);
    double t = 0;
    //A += 0.5*osg::PI;
    //t = A / osg::PI;
    //std::cout<<"SDFDofRange::computeRangeT T="<<t<<std::endl;
    return A;
}

void SDFDofRange::setIsValid(bool boolValue)
{
    m_isValid = boolValue;
}

